package drds.global_transaction.transaction_coordinator.transaction_session;

import drds.global_transaction.api.api.BranchTransactionStatus;
import drds.global_transaction.api.exception.GlobalTransactionException;
import drds.global_transaction.transaction_coordinator.lock_manager.LockManagerFactory;
import drds.global_transaction.transaction_coordinator.store.SessionStorable;
import lombok.Getter;
import lombok.Setter;

import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class BranchTransactionSession implements Lockable, Comparable<BranchTransactionSession>, SessionStorable
{

	@Setter
	@Getter
	private long globalTransactionId;

	@Setter
	@Getter
	private long branchTransactionId;

	@Setter
	@Getter
	private String resourceGroupId;
	@Setter
	@Getter
	private String resourceId;
	@Setter
	@Getter
	private String tableNameAndLockRecordIdsStringsString;// lock ids

	@Setter
	@Getter
	private BranchTransactionStatus branchTransactionStatus = BranchTransactionStatus.unknown;
	@Setter
	@Getter
	private String applicationId;

	@Setter
	@Getter
	private String transactionServiceGroup;
	@Setter
	@Getter
	private String clientIp;
	@Setter
	@Getter
	private String applicationData;
	/**
	 * 分支事务 lock容器:bucketLockMap关联主lockMap
	 */
	@Setter
	@Getter
	private ConcurrentHashMap<Map<String, Long>, Set<String>> bucketLockMapToLockRecordIdSetMap = new ConcurrentHashMap<Map<String, Long>, Set<String>>();

	@Override
	public int compareTo(BranchTransactionSession o)
	{
		return this.branchTransactionId < o.branchTransactionId ? -1 : (this.branchTransactionId > o.branchTransactionId ? 1 : 0);
	}

	@Override
	public void decode(byte[] bytes)
	{
		ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
		this.globalTransactionId = byteBuffer.getLong();
		this.branchTransactionId = byteBuffer.getLong();
		int resourceLength = byteBuffer.getInt();
		if (resourceLength > 0)
		{
			byte[] $bytes = new byte[resourceLength];
			byteBuffer.get($bytes);
			this.resourceId = new String($bytes);
		}
		int lockKeyLen = byteBuffer.getInt();
		if (lockKeyLen > 0)
		{
			byte[] $bytes = new byte[lockKeyLen];
			byteBuffer.get($bytes);
			this.tableNameAndLockRecordIdsStringsString = new String($bytes);
		}
		short applicationIdLen = byteBuffer.getShort();
		if (applicationIdLen > 0)
		{
			byte[] $bytes = new byte[applicationIdLen];
			byteBuffer.get($bytes);
			this.applicationId = new String($bytes);
		}
		short txServiceGroupLen = byteBuffer.getShort();
		if (txServiceGroupLen > 0)
		{
			byte[] $bytes = new byte[txServiceGroupLen];
			byteBuffer.get($bytes);
			this.transactionServiceGroup = new String($bytes);
		}
		short clientIpLength = byteBuffer.getShort();
		if (clientIpLength > 0)
		{
			byte[] $bytes = new byte[clientIpLength];
			byteBuffer.get($bytes);
			this.clientIp = new String($bytes);
		}
		int applicationDataLength = byteBuffer.getInt();
		if (applicationDataLength > 0)
		{
			byte[] $bytes = new byte[applicationDataLength];
			byteBuffer.get($bytes);
			this.applicationData = new String($bytes);
		}

	}

	@Override
	public byte[] encode()
	{
		ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
		byteBuffer.putLong(globalTransactionId);
		byteBuffer.putLong(branchTransactionId);
		if (null != resourceId)
		{
			byte[] bytes = resourceId.getBytes();
			byteBuffer.putInt(bytes.length);
			byteBuffer.put(bytes);
		} else
		{
			byteBuffer.putInt(0);
		}
		if (null != tableNameAndLockRecordIdsStringsString)
		{
			byte[] bytes = tableNameAndLockRecordIdsStringsString.getBytes();
			byteBuffer.putInt(bytes.length);
			byteBuffer.put(bytes);
		} else
		{
			byteBuffer.putInt(0);
		}
		if (null != applicationId)
		{
			byte[] bytes = applicationId.getBytes();
			byteBuffer.putShort((short) bytes.length);
			byteBuffer.put(bytes);
		} else
		{
			byteBuffer.putShort((short) 0);
		}
		if (null != transactionServiceGroup)
		{
			byte[] bytes = transactionServiceGroup.getBytes();
			byteBuffer.putShort((short) bytes.length);
			byteBuffer.put(bytes);
		} else
		{
			byteBuffer.putShort((short) 0);
		}
		if (null != clientIp)
		{
			byte[] bytes = clientIp.getBytes();
			byteBuffer.putShort((short) bytes.length);
			byteBuffer.put(bytes);
		} else
		{
			byteBuffer.putShort((short) 0);
		}
		if (null != applicationData)
		{
			byte[] bytes = applicationData.getBytes();
			byteBuffer.putInt(bytes.length);
			byteBuffer.put(bytes);
		} else
		{
			byteBuffer.putInt(0);
		}
		byteBuffer.flip();
		byte[] result = new byte[byteBuffer.limit()];
		byteBuffer.get(result);
		return result;
	}

	@Override
	public boolean lock() throws GlobalTransactionException
	{
		return LockManagerFactory.get().acquireLock(this);
	}

	@Override
	public boolean unlock() throws GlobalTransactionException
	{
		if (bucketLockMapToLockRecordIdSetMap.size() == 0)
		{
			return true;
		}
		Iterator<Map.Entry<Map<String, Long>, Set<String>>> iterator = bucketLockMapToLockRecordIdSetMap.entrySet().iterator();
		while (iterator.hasNext())
		{
			Map.Entry<Map<String, Long>, Set<String>> entry = iterator.next();
			Map<String, Long> bucketLockMap = entry.getKey();
			Set<String> lockRecordIdSet = entry.getValue();
			//
			synchronized (bucketLockMap)
			{
				for (String key : lockRecordIdSet)
				{
					Long value = bucketLockMap.get(key);
					if (value == null)
					{
						continue;
					}
					// value=全局事务Id
					if (value.longValue() == getGlobalTransactionId())
					{
						bucketLockMap.remove(key);
					}
				}
			}
		}
		bucketLockMapToLockRecordIdSetMap.clear();
		return true;
	}

}
